package my2Cents;

import java.awt.geom.GeneralPath;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import my2Cents.tools.AbstractPhysTool;
import net.phys2d.math.Vector2f;
import net.phys2d.raw.BasicJoint;
import net.phys2d.raw.Body;
import net.phys2d.raw.CollisionEvent;
import net.phys2d.raw.Joint;
import net.phys2d.raw.StaticBody;
import net.phys2d.raw.World;
import net.phys2d.raw.shapes.PBox;
import net.phys2d.raw.shapes.PCircle;
import net.phys2d.raw.shapes.PhysShape;

public class Util {
	private static Body handle;
	private static Vector<Body> s_ExcludeList = new Vector<Body>();
	
	public static String getJustClassName(Class c) {
		String name = c.getName();
		//String name = "test,test,test";
		
		int index = name.lastIndexOf(".");
		name = name.substring(index+1);
		return name;
	}
	
	public static Class[] getClasses(String pckgname, Class call)
	throws ClassNotFoundException {
		ArrayList<Class> classes = new ArrayList<Class>();
//		Get a File object for the package
		File directory = null;
		ClassLoader cld = Thread.currentThread().getContextClassLoader();
		try {
			if (cld == null) {
				throw new ClassNotFoundException("Can't get class loader.");
			}
			String path = pckgname.replace('.', '/');
			URL resource = cld.getResource(path);
			if (resource == null) {
				Sandbox.out.println("No resource for " + path);
				
				throw new ClassNotFoundException("No resource for " + path);
			}
			String f = resource.getFile().substring(1);
			f = f.replace("%20"," ");
			f = f.replace("ile:/", "");
			f = f.replace("file:/", "");
//			f = f.replace("!", "");
//			f = f.replace("PhysBox.jar", "");
			Sandbox.out.println(f);
			if(f.contains(".jar")) {
				f = "./bin/my2cents/tools";
			} else {
				f = "./bin/my2cents/tools";
			}
			
			String f2 = call.getResource("").getPath();
			f2 = f2.substring(1);
			f2 = f2.substring(0, f2.length()-9);
			System.out.println(f2 + path);
			directory = new File(f);
			Sandbox.out.println(directory.getPath());
		} catch (NullPointerException x) {
			Sandbox.out.println(pckgname + " (" + directory
					+ ") does not appear to be a valid package");
			
			throw new ClassNotFoundException(pckgname + " (" + directory
					+ ") does not appear to be a valid package");
		}
		if (directory.exists()) {
			// Get the list of the files contained in the package
			String[] files = directory.list();
			for (int i = 0; i < files.length; i++) {
				// we are only interested in .class files
				if (files[i].endsWith(".class")) {
					// removes the .class extension
					classes.add(Class.forName(pckgname + '.' + files[i].substring(0, files[i].length() - 6),true,cld));
				}
			}
		} else {
			throw new ClassNotFoundException(pckgname
					+ " does not appear to be a valid package");
		}
		Class[] classesA = new Class[classes.size()];
		classes.toArray(classesA);
		return classesA;
	}
	
	public static Body selectBody(float x, float y, World w) {
		Body currentBody = null;
		boolean handleIsNew = false;
		if(handle == null) {
			handle = new StaticBody(new PBox(10,10));
			handleIsNew = true;
		}
		handle.setPosition(x,y);
		w.getBodyStateManager().excludeBody(handle);
		w.getBodyStateManager().saveBodyStates(AbstractPhysTool.BODYSTATES_TOOL,w);
		w.getBodyStateManager().reIncludeBody(handle);
		if(handleIsNew) w.add(handle);
		w.step();
		for(int i=0; i<w.getBodies().size(); i++) {
			Body b = w.getBodies().get(i);
			Vector2f v = new Vector2f(x,y);
			if(v.distance(b.getPosition()) < 8 && b != handle &&
					!s_ExcludeList.contains(b)) {
				currentBody = b;
				currentBody.addExcludedBody(handle);
				w.clearArbiters(currentBody);
				w.step();
				restoreBodyStates(w);
				clearBodyCollisions(handle,w);
				return currentBody;
			}
			if(b != handle && !s_ExcludeList.contains(b)) {
				PhysShape shape = b.getShape();
				GeneralPath path = shape.getGeneralPath(
						(Vector2f) b.getPosition(), 
						b.getRotation());
				
				Vector2f hpos = (Vector2f) handle.getPosition();
				if(path.contains(hpos.x, hpos.y)) {
					currentBody = b;
					b.addExcludedBody(handle);
					w.clearArbiters(currentBody);
					w.step();
					restoreBodyStates(w);
					clearBodyCollisions(handle,w);
					return currentBody;
				}
			}
		}
		CollisionEvent[] eventList = w.getContacts(handle);
		if(eventList != null && eventList.length > 0) {
			for(int i=0; i<eventList.length; i++) {
				CollisionEvent event = eventList[i];
				if(!s_ExcludeList.contains(event.getBodyA()) &&
				   !s_ExcludeList.contains(event.getBodyB())) {
					if(event.getBodyA() == handle) {
						currentBody = event.getBodyB();
					} else if(event.getBodyB() == handle) {
						currentBody = event.getBodyA();
					}
				}
			}
			if(currentBody != null) {
				currentBody.addExcludedBody(handle);
				w.clearArbiters(currentBody);
				w.step();
			}
		}
		restoreBodyStates(w);
		clearBodyCollisions(handle,w);
		return currentBody;
	}
	
	private static World getWorld() {
		return Sandbox.getWorld();
	}
	
	public static StaticBody createAnchorBody(Vector2f pos) {
		PhysShape shape = new PCircle(0.5f);
		StaticBody b = new StaticBody(shape);
		b.setPosition(pos.x,pos.y);
		getWorld().add(b);
		return b;
	}
	
	public static BasicJoint setupHinge(Vector2f start, Vector2f end, boolean snap, boolean ncollide, boolean autoanchor, boolean createUndos) {
		Body b1 = Util.selectBody(start.x,start.y,getWorld());
		Util.addToS_ExcludeList(b1);
		Util.clearHandle(getWorld());
		Body b2 = Util.selectBody(end.x,end.y,getWorld());
		Util.addToS_ExcludeList(b2);
		Util.clearHandle(getWorld());
		Util.clearS_ExcludeList();
		
		if(snap) {
			if(b1 != null) start = (Vector2f) b1.getPosition();
			if(b2 != null) end = (Vector2f) b2.getPosition();
		}
		if(ncollide) {
			if(b1 != null && b2 != null) {
				b1.addExcludedBody(b2);
				b2.addExcludedBody(b1);
			}
		}
		if(b1 != null) {
			boolean anchored = false;
			if(autoanchor) {
				if(b2 == null || b2 == b1) {
					b2 = createAnchorBody(end);
					b1.addExcludedBody(b2);
					b2.setShouldCollide(false);
					anchored = true;
				}
			}
			if(b1 != null && b2 != null && b1 != b2) {
				BasicJoint hinge = new BasicJoint(b1,b2,start);
				if(hinge != null) {
					if(createUndos) {
						Undo undo = new Undo(hinge,getWorld(),"Constraint");
						if(anchored) undo.groupUndo(new Undo(b2,getWorld(),"Anchor"));
						Sandbox.getUndoList().add(undo);
					}
					getWorld().add(hinge);
					return hinge;
				}
			}
		}
		return null;
	}
	
	public static void addToS_ExcludeList(Body b) {
		if(b != null) {
			s_ExcludeList.add(b);
		}
	}
	
	public static void clearS_ExcludeList() {
		s_ExcludeList.clear();
	}
	
	public static void clearBodyCollisions(Body tc, World w) {
		for(int i=0; i<w.getBodies().size(); i++) {
			Body b = w.getBodies().get(i);
			tc.addExcludedBody(b);
		}
		w.clearArbiters(tc);
	}
	
	public static void restoreBodyStates(World w) {
		w.getBodyStateManager().restoreBodyStates(AbstractPhysTool.BODYSTATES_TOOL, w);
	}
	
	public static void clearHandle(World w) {
		if(handle != null) {
			w.remove(handle);
			handle = null;
		}
	}
	
	public static Body getHandle() {
		return handle;
	}
	
	public static boolean saveWorld(File file, World w) {
        ObjectOutput out;
        String fstr = file.getPath() + ".world_" + Sandbox.VERSION;
        if(file.exists()) file.delete();
		try {
			out = new ObjectOutputStream(new FileOutputStream(fstr));
	        out.writeObject(w);
	        out.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		World wload = loadWorld(new File(fstr));
		if(wload == null) {
			return false;
		}
		return true;
	}
	
	public static World loadWorld(File file) {
		World world = null;
	    try {
	        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
	        // Deserialize the object
	        world = (World) in.readObject();
	        in.close();

	    } catch (ClassNotFoundException e) {
	    } catch (IOException e) {
	    }
		return world;
	}

	private byte[] getBytesFromFile(File file) {
		// TODO Auto-generated method stub
		return null;
	}
	
	public static Body makeLine(Vector2f start, Vector2f end, float width, float mass) {
		PBox shape = null;
		float x1 = start.x;
		float y1 = start.y;
		
		double angle = angleVec(start,end);
		float distance = start.distance(end);
		float cx = (float) (Math.cos(angle)*distance/2);
		float cy = (float) (Math.sin(angle)*distance/2);
		
		start.x = -cx;
		start.y = -cy;
		end.x = cx;
		end.y = cy;
		
		shape = new PBox(distance,width);
		Body b = new Body(shape,mass);
		b.setRotation((float) angle);
		b.setPosition(x1 + cx, y1 + cy);
		
		return b;
	}
	
	public static double angleVec(Vector2f startPos, Vector2f endPos) {
		double dx = endPos.x - startPos.x;
		double dy = endPos.y - startPos.y;
		double angle = Math.atan2(dy,dx);
		return angle;
	}
}
